package Euclid;

import java.math.*;

public class IntVarType extends VarType {
	
	public BigInteger val;
	
	public int type = TYPE_INT;
	
	private MathContext mc = MathContext.DECIMAL32;
	
	public IntVarType(String v) { val = new BigInteger(v); formula = v; }
	public IntVarType(BigInteger v, String f) { val = v; formula = f; }
	
	public VarType add(VarType o) {
		if (o instanceof IntVarType)
			return new IntVarType(val.add(((IntVarType)o).val),
					formula + " + " + o.formula);
		else if (o instanceof FloatVarType)
			return new FloatVarType(new BigDecimal(val).add(((FloatVarType)o).val, mc),
					formula + " + " + o.formula);
		else if (o instanceof SymbVarType)
			return add(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation $+$ unsupported on type " + o.getClass());
	}
	
	public VarType subtract(VarType o) {
		if (o instanceof IntVarType)
			return new IntVarType(val.subtract(((IntVarType)o).val),
					formula + " - " + o.formula);
		else if (o instanceof FloatVarType)
			return new FloatVarType(new BigDecimal(val).subtract(((FloatVarType)o).val, mc),
					formula + " - " + o.formula);
		else if (o instanceof SymbVarType)
			return subtract(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation $-$ unsupported on type " + o.getClass());
	}
	
	public VarType mult(VarType o) {
		if (o instanceof IntVarType)
			return new IntVarType(val.multiply(((IntVarType)o).val),
					formula + " \\times " + o.formula);
		else if (o instanceof FloatVarType)
			return new FloatVarType(new BigDecimal(val).multiply(((FloatVarType)o).val, mc),
					formula + " \\times " + o.formula);
		else if (o instanceof SymbVarType)
			return mult(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation $\\times$ unsupported on type " + o.getClass());
	}
	
	public VarType div(VarType o) {
		if (o instanceof IntVarType) {
			// if it divides cleanly use IntVarType
			if (val.mod(((IntVarType)o).val).compareTo(BigInteger.ZERO) == 0)
				return new IntVarType(val.divide(((IntVarType)o).val),
					" \\frac{" + formula + "}{" + o.formula + "} ");
			else
				return new FloatVarType(new BigDecimal(val).divide(new BigDecimal(((IntVarType)o).val), mc),
					" \\frac{" + formula + "}{" + o.formula + "} ");
		}
		else if (o instanceof FloatVarType)
			return new FloatVarType(new BigDecimal(val).divide(((FloatVarType)o).val, mc),
					" \\frac{" + formula + "}{" + o.formula + "} ");
		else if (o instanceof SymbVarType)
			return div(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation $\\div$ unsupported on type " + o.getClass());
	}
	
	public VarType mod(VarType o) {
		if (o instanceof IntVarType)
			return new IntVarType(val.mod(((IntVarType)o).val),
					formula + "\\pmod{" + o.formula + "}");
		else if (o instanceof FloatVarType)
			return null; // error
		else if (o instanceof SymbVarType)
			return mod(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation \\textbf{mod} unsupported on type " + o.getClass());
	}
	
	public VarType pow(VarType o) {
		if (o instanceof IntVarType)
			return new IntVarType(val.pow(((IntVarType)o).val.intValue()), 
					formula + "^{" + o.formula + "}");
		else if (o instanceof SymbVarType)
			return pow(((SymbVarType)o).val);
		else if (o instanceof ErrorVarType)
			return o;
		else
			return new ErrorVarType("Operation $x^y$ unsupported on type " + o.getClass());
	}
	
	public VarType factorial() {
		return new IntVarType(do_factorial(val), formula + " ! ");
	}
	
	public VarType negate() {
		return new IntVarType(val.negate(), " - " + formula);
	}
	
	public VarType not() {
		if (val.compareTo(BigInteger.ZERO) == 0)
			return new IntVarType(BigInteger.ONE, " \\sim " + formula);
		else
			return new IntVarType(BigInteger.ZERO, " \\sim " + formula);
	}
	
	public String closeFormula() {
		return formula + " = " + val.toString();
	}
	
	private BigInteger do_factorial(BigInteger v)
	{
		BigInteger val = BigInteger.ONE;
		BigInteger counter = v;
		while (counter.compareTo(BigInteger.ONE) > 0) {
			val = val.multiply(counter);
			counter = counter.subtract(BigInteger.ONE);
		}
		return val;
	}
	
	public String toString() {
		return val.toString();
	}
}